package cn.francis.encrypt.interceptor;

import cn.francis.encrypt.annotation.EncryptEntity;
import cn.francis.encrypt.properties.EncryptProperties;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.crypto.SmUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

/**
 * @author 张俊辉
 */

@Component
@RequiredArgsConstructor
public class EncryptInterceptor {

    private static final List<String> NOT_ENCRYPT_FIELD = Arrays.asList("delFlag", "createTime", "updateTime", "serialVersionUID");

    private final EncryptProperties encryptProperties;

    /**
     * 处理实体类型信息
     */
    public void disposeEntity(Object parameterObject) throws IllegalAccessException {
        Class<?> parameterObjectClass = parameterObject.getClass();

        if (List.class.isAssignableFrom(parameterObjectClass)) {
            List<?> data = (List<?>) parameterObject;
            for (Object object : data) {
                extract(parameterObject, object.getClass());
            }
        }
    }

    /**
     * 萃取
     */
    private void extract(Object parameterObject, Class<?> parameterObjectClass) throws IllegalAccessException {
        EncryptEntity annotation = AnnotationUtils.findAnnotation(parameterObjectClass, EncryptEntity.class);
        if (ObjectUtil.isEmpty(annotation)) {
            return;
        }
        Field[] declaredFields = parameterObjectClass.getDeclaredFields();
        for (Field field : declaredFields) {
            disposeFiled(parameterObject, field);
        }
    }

    /**
     * 处理字段信息
     */
    private void disposeFiled(Object parameterObject, Field field) throws IllegalAccessException {
        if (NOT_ENCRYPT_FIELD.contains(field.getName())) {
            return;
        }
        EncryptEntity sensitiveFiled = field.getAnnotation(EncryptEntity.class);
        ReflectUtil.setAccessible(field);

        if (ObjectUtil.isNull(sensitiveFiled)) {
            return;
        }

        Object filedValue = field.get(parameterObject);
        if (filedValue == null) {
            return;
        }

        if (filedValue instanceof String value) {
            if (CharSequenceUtil.isEmpty(value)) {
                return;
            }
            field.set(parameterObject, SmUtil.sm4(encryptProperties.getSalt().getBytes(StandardCharsets.UTF_8)).encryptHex(value));
        }
    }
}
